From e16fc5ba591cd0d8179193b020079dcc25f3d4c9 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Tue, 10 Feb 2009 05:51:00 +0000 Subject: [PATCH] x86: mce: Provide extended physical CPU info. Provide extended physial CPU info for the sake of dom0 MCE handling. This information includes info for all logical CPUs, cpuid information from all of them, and initial MSR values for a few MSRs that are important to MCE handling. Signed-off-by: Frank van der Linden --- xen/arch/x86/cpu/mcheck/amd_k8.c | 14 +-- xen/arch/x86/cpu/mcheck/amd_nonfatal.c | 13 ++- xen/arch/x86/cpu/mcheck/mce.c | 130 +++++++++++++++++++++++++ xen/arch/x86/cpu/mcheck/mce.h | 3 +- xen/arch/x86/cpu/mcheck/mce_intel.c | 8 +- xen/include/public/arch-x86/xen-mca.h | 48 ++++++++- 6 files changed, 194 insertions(+), 22 deletions(-) diff --git a/xen/arch/x86/cpu/mcheck/amd_k8.c b/xen/arch/x86/cpu/mcheck/amd_k8.c index 8a4ed48073..768bfadb2a 100644 --- a/xen/arch/x86/cpu/mcheck/amd_k8.c +++ b/xen/arch/x86/cpu/mcheck/amd_k8.c @@ -99,6 +99,8 @@ void k8_machine_check(struct cpu_user_regs *regs, long error_code) mc_data = x86_mcinfo_getptr(); cpu_nr = smp_processor_id(); + BUG_ON(cpu_nr != vcpu->processor); + curdom = vcpu->domain; memset(&mc_global, 0, sizeof(mc_global)); @@ -106,14 +108,12 @@ void k8_machine_check(struct cpu_user_regs *regs, long error_code) mc_global.common.size = sizeof(mc_global); mc_global.mc_domid = curdom->domain_id; /* impacted domain */ - mc_global.mc_coreid = vcpu->processor; /* impacted physical cpu */ - BUG_ON(cpu_nr != vcpu->processor); - mc_global.mc_core_threadid = 0; + + x86_mc_get_cpu_info(cpu_nr, &mc_global.mc_socketid, + &mc_global.mc_coreid, &mc_global.mc_core_threadid, + &mc_global.mc_apicid, NULL, NULL, NULL); + mc_global.mc_vcpuid = vcpu->vcpu_id; /* impacted vcpu */ -#if 0 /* TODO: on which socket is this physical core? - It's not clear to me how to figure this out. */ - mc_global.mc_socketid = ???; -#endif mc_global.mc_flags |= MC_FLAG_UNCORRECTABLE; rdmsrl(MSR_IA32_MCG_STATUS, mc_global.mc_gstatus); diff --git a/xen/arch/x86/cpu/mcheck/amd_nonfatal.c b/xen/arch/x86/cpu/mcheck/amd_nonfatal.c index 03827fac5f..f57e4e3811 100644 --- a/xen/arch/x86/cpu/mcheck/amd_nonfatal.c +++ b/xen/arch/x86/cpu/mcheck/amd_nonfatal.c @@ -95,6 +95,7 @@ void mce_amd_checkregs(void *info) mc_data = NULL; cpu_nr = smp_processor_id(); + BUG_ON(cpu_nr != vcpu->processor); event_enabled = guest_enabled_event(dom0->vcpu[0], VIRQ_MCA); error_found = 0; @@ -103,14 +104,12 @@ void mce_amd_checkregs(void *info) mc_global.common.size = sizeof(mc_global); mc_global.mc_domid = vcpu->domain->domain_id; /* impacted domain */ - mc_global.mc_coreid = vcpu->processor; /* impacted physical cpu */ - BUG_ON(cpu_nr != vcpu->processor); - mc_global.mc_core_threadid = 0; mc_global.mc_vcpuid = vcpu->vcpu_id; /* impacted vcpu */ -#if 0 /* TODO: on which socket is this physical core? - It's not clear to me how to figure this out. */ - mc_global.mc_socketid = ???; -#endif + + x86_mc_get_cpu_info(cpu_nr, &mc_global.mc_socketid, + &mc_global.mc_coreid, &mc_global.mc_core_threadid, + &mc_global.mc_apicid, NULL, NULL, NULL); + mc_global.mc_flags |= MC_FLAG_CORRECTABLE; rdmsrl(MSR_IA32_MCG_STATUS, mc_global.mc_gstatus); diff --git a/xen/arch/x86/cpu/mcheck/mce.c b/xen/arch/x86/cpu/mcheck/mce.c index ebc222dc49..3b622adc50 100644 --- a/xen/arch/x86/cpu/mcheck/mce.c +++ b/xen/arch/x86/cpu/mcheck/mce.c @@ -443,6 +443,96 @@ next: +static void do_mc_get_cpu_info(void *v) +{ + int cpu = smp_processor_id(); + int cindex, cpn; + struct cpuinfo_x86 *c; + xen_mc_logical_cpu_t *log_cpus, *xcp; + uint32_t junk, ebx; + + log_cpus = v; + c = &cpu_data[cpu]; + cindex = 0; + cpn = cpu - 1; + + /* + * Deal with sparse masks, condensed into a contig array. + */ + while (cpn >= 0) { + if (cpu_isset(cpn, cpu_online_map)) + cindex++; + cpn--; + } + + xcp = &log_cpus[cindex]; + c = &cpu_data[cpu]; + xcp->mc_cpunr = cpu; + x86_mc_get_cpu_info(cpu, &xcp->mc_chipid, + &xcp->mc_coreid, &xcp->mc_threadid, + &xcp->mc_apicid, &xcp->mc_ncores, + &xcp->mc_ncores_active, &xcp->mc_nthreads); + xcp->mc_cpuid_level = c->cpuid_level; + xcp->mc_family = c->x86; + xcp->mc_vendor = c->x86_vendor; + xcp->mc_model = c->x86_model; + xcp->mc_step = c->x86_mask; + xcp->mc_cache_size = c->x86_cache_size; + xcp->mc_cache_alignment = c->x86_cache_alignment; + memcpy(xcp->mc_vendorid, c->x86_vendor_id, sizeof xcp->mc_vendorid); + memcpy(xcp->mc_brandid, c->x86_model_id, sizeof xcp->mc_brandid); + memcpy(xcp->mc_cpu_caps, c->x86_capability, sizeof xcp->mc_cpu_caps); + + /* + * This part needs to run on the CPU itself. + */ + xcp->mc_nmsrvals = __MC_NMSRS; + xcp->mc_msrvalues[0].reg = MSR_IA32_MCG_CAP; + rdmsrl(MSR_IA32_MCG_CAP, xcp->mc_msrvalues[0].value); + + if (c->cpuid_level >= 1) { + cpuid(1, &junk, &ebx, &junk, &junk); + xcp->mc_clusterid = (ebx >> 24) & 0xff; + } else + xcp->mc_clusterid = hard_smp_processor_id(); +} + + +void x86_mc_get_cpu_info(unsigned cpu, uint32_t *chipid, uint16_t *coreid, + uint16_t *threadid, uint32_t *apicid, + unsigned *ncores, unsigned *ncores_active, + unsigned *nthreads) +{ + struct cpuinfo_x86 *c; + + *apicid = cpu_physical_id(cpu); + c = &cpu_data[cpu]; + if (c->apicid == BAD_APICID) { + *chipid = cpu; + *coreid = 0; + *threadid = 0; + if (ncores != NULL) + *ncores = 1; + if (ncores_active != NULL) + *ncores_active = 1; + if (nthreads != NULL) + *nthreads = 1; + } else { + *chipid = phys_proc_id[cpu]; + if (c->x86_max_cores > 1) + *coreid = cpu_core_id[cpu]; + else + *coreid = 0; + *threadid = c->apicid & ((1 << (c->x86_num_siblings - 1)) - 1); + if (ncores != NULL) + *ncores = c->x86_max_cores; + if (ncores_active != NULL) + *ncores_active = c->booted_cores; + if (nthreads != NULL) + *nthreads = c->x86_num_siblings; + } +} + /* Machine Check Architecture Hypercall */ long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc) { @@ -452,6 +542,7 @@ long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc) struct domain *domU; struct xen_mc_fetch *mc_fetch; struct xen_mc_notifydomain *mc_notifydomain; + struct xen_mc_physcpuinfo *mc_physcpuinfo; struct mc_info *mi; uint32_t flags; uint32_t fetch_idx; @@ -460,6 +551,8 @@ long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc) * a DomU to fetch mc data while Dom0 notifies another DomU. */ static DEFINE_SPINLOCK(mc_lock); static DEFINE_SPINLOCK(mc_notify_lock); + int nlcpu; + xen_mc_logical_cpu_t *log_cpus = NULL; if ( copy_from_guest(op, u_xen_mc, 1) ) return -EFAULT; @@ -580,6 +673,43 @@ long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc) spin_unlock(&mc_notify_lock); break; + + case XEN_MC_physcpuinfo: + if ( !IS_PRIV(v->domain) ) + return -EPERM; + + mc_physcpuinfo = &op->u.mc_physcpuinfo; + nlcpu = num_online_cpus(); + + if (!guest_handle_is_null(mc_physcpuinfo->info)) { + if (mc_physcpuinfo->ncpus <= 0) + return -EINVAL; + nlcpu = min(nlcpu, (int)mc_physcpuinfo->ncpus); + log_cpus = xmalloc_array(xen_mc_logical_cpu_t, nlcpu); + if (log_cpus == NULL) + return -ENOMEM; + + if (on_each_cpu(do_mc_get_cpu_info, log_cpus, + 1, 1) != 0) { + xfree(log_cpus); + return -EIO; + } + } + + mc_physcpuinfo->ncpus = nlcpu; + + if (copy_to_guest(u_xen_mc, op, 1)) { + if (log_cpus != NULL) + xfree(log_cpus); + return -EFAULT; + } + + if (!guest_handle_is_null(mc_physcpuinfo->info)) { + if (copy_to_guest(mc_physcpuinfo->info, + log_cpus, nlcpu)) + ret = -EFAULT; + xfree(log_cpus); + } } return ret; diff --git a/xen/arch/x86/cpu/mcheck/mce.h b/xen/arch/x86/cpu/mcheck/mce.h index d588a6dc05..f2e29897bd 100644 --- a/xen/arch/x86/cpu/mcheck/mce.h +++ b/xen/arch/x86/cpu/mcheck/mce.h @@ -34,4 +34,5 @@ void x86_mcinfo_clear(struct mc_info *mi); int x86_mcinfo_add(struct mc_info *mi, void *mcinfo); void x86_mcinfo_dump(struct mc_info *mi); void mc_panic(char *s); - +void x86_mc_get_cpu_info(unsigned, uint32_t *, uint16_t *, uint16_t *, + uint32_t *, uint32_t *, uint32_t *, uint32_t *); diff --git a/xen/arch/x86/cpu/mcheck/mce_intel.c b/xen/arch/x86/cpu/mcheck/mce_intel.c index e02f20f6f5..7a6cf8facd 100644 --- a/xen/arch/x86/cpu/mcheck/mce_intel.c +++ b/xen/arch/x86/cpu/mcheck/mce_intel.c @@ -182,11 +182,9 @@ static struct mc_info *machine_check_poll(int calltype) mcg.mc_flags = MC_FLAG_POLLED; else if (calltype == MC_FLAG_CMCI) mcg.mc_flags = MC_FLAG_CMCI; - mcg.mc_socketid = phys_proc_id[cpu]; - mcg.mc_coreid = cpu_core_id[cpu]; - mcg.mc_apicid = cpu_physical_id(cpu); - mcg.mc_core_threadid = - mcg.mc_apicid & ( 1 << (cpu_data[cpu].x86_num_siblings - 1)); + x86_mc_get_cpu_info( + cpu, &mcg.mc_socketid, &mcg.mc_coreid, + &mcg.mc_core_threadid, &mcg.mc_apicid, NULL, NULL, NULL); rdmsrl(MSR_IA32_MCG_STATUS, mcg.mc_gstatus); for ( i = 0; i < nr_mce_banks; i++ ) { diff --git a/xen/include/public/arch-x86/xen-mca.h b/xen/include/public/arch-x86/xen-mca.h index 60ae871947..e1f5297bfa 100644 --- a/xen/include/public/arch-x86/xen-mca.h +++ b/xen/include/public/arch-x86/xen-mca.h @@ -56,7 +56,7 @@ /* Hypercall */ #define __HYPERVISOR_mca __HYPERVISOR_arch_0 -#define XEN_MCA_INTERFACE_VERSION 0x03000001 +#define XEN_MCA_INTERFACE_VERSION 0x03000002 /* IN: Dom0 calls hypercall from MC event handler. */ #define XEN_MC_CORRECTABLE 0x0 @@ -118,7 +118,7 @@ struct mcinfo_global { uint16_t mc_domid; uint32_t mc_socketid; /* physical socket of the physical core */ uint16_t mc_coreid; /* physical impacted core */ - uint8_t mc_apicid; + uint32_t mc_apicid; uint16_t mc_core_threadid; /* core thread of physical core */ uint16_t mc_vcpuid; /* virtual cpu scheduled for mc_domid */ uint64_t mc_gstatus; /* global status */ @@ -175,6 +175,41 @@ struct mc_info { }; typedef struct mc_info mc_info_t; +#define __MC_MSR_ARRAYSIZE 8 +#define __MC_NMSRS 1 +#define MC_NCAPS 7 /* 7 CPU feature flag words */ +#define MC_CAPS_STD_EDX 0 /* cpuid level 0x00000001 (%edx) */ +#define MC_CAPS_AMD_EDX 1 /* cpuid level 0x80000001 (%edx) */ +#define MC_CAPS_TM 2 /* cpuid level 0x80860001 (TransMeta) */ +#define MC_CAPS_LINUX 3 /* Linux-defined */ +#define MC_CAPS_STD_ECX 4 /* cpuid level 0x00000001 (%ecx) */ +#define MC_CAPS_VIA 5 /* cpuid level 0xc0000001 */ +#define MC_CAPS_AMD_ECX 6 /* cpuid level 0x80000001 (%ecx) */ + +typedef struct mcinfo_logical_cpu { + uint32_t mc_cpunr; + uint32_t mc_chipid; + uint16_t mc_coreid; + uint16_t mc_threadid; + uint32_t mc_apicid; + uint32_t mc_clusterid; + uint32_t mc_ncores; + uint32_t mc_ncores_active; + uint32_t mc_nthreads; + int32_t mc_cpuid_level; + uint32_t mc_family; + uint32_t mc_vendor; + uint32_t mc_model; + uint32_t mc_step; + char mc_vendorid[16]; + char mc_brandid[64]; + uint32_t mc_cpu_caps[MC_NCAPS]; + uint32_t mc_cache_size; + uint32_t mc_cache_alignment; + int32_t mc_nmsrvals; + struct mcinfo_msr mc_msrvalues[__MC_MSR_ARRAYSIZE]; +} xen_mc_logical_cpu_t; +DEFINE_XEN_GUEST_HANDLE(xen_mc_logical_cpu_t); /* @@ -272,6 +307,14 @@ struct xen_mc_notifydomain { typedef struct xen_mc_notifydomain xen_mc_notifydomain_t; DEFINE_XEN_GUEST_HANDLE(xen_mc_notifydomain_t); +#define XEN_MC_physcpuinfo 3 +struct xen_mc_physcpuinfo { + /* IN/OUT */ + uint32_t ncpus; + uint32_t pad0; + /* OUT */ + XEN_GUEST_HANDLE(xen_mc_logical_cpu_t) info; +}; struct xen_mc { uint32_t cmd; @@ -279,6 +322,7 @@ struct xen_mc { union { struct xen_mc_fetch mc_fetch; struct xen_mc_notifydomain mc_notifydomain; + struct xen_mc_physcpuinfo mc_physcpuinfo; uint8_t pad[MCINFO_HYPERCALLSIZE]; } u; }; -- 2.30.2